home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / xvisrc.zip / NORMAL.C < prev    next >
C/C++ Source or Header  |  1992-07-28  |  42KB  |  1,821 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)normal.c    2.7 (Chris & John Downey) 8/24/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     normal.c
  14. * module function:
  15.     Main routine for processing characters in command mode
  16.     as well as routines for handling the operators.
  17. * history:
  18.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  19.     Originally by Tim Thompson (twitch!tjt)
  20.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  21.     Heavily modified by Chris & John Downey
  22.  
  23. ***/
  24.  
  25. #include "xvi.h"
  26.  
  27. static    bool_t    do_target P((int, int));
  28. static    bool_t    do_cmd P((int, int));
  29. static    bool_t    do_badcmd P((int, int));
  30. static    bool_t    do_page P((int, int));
  31. static    bool_t    do_scroll P((int, int));
  32. static    bool_t    do_word P((int, int));
  33. static    bool_t    do_csearch P((int, int));
  34. static    bool_t    do_z P((int, int));
  35. static    bool_t    do_x P((int, int));
  36. static    bool_t    do_HLM P((int, int));
  37. static    bool_t    do_rchar P((int, int));
  38. static    bool_t    do_ins P((int, int));
  39. static    void    op_shift P((int, int, int, long, Posn *, Posn *));
  40. static    void    op_delete P((int, int, long, Posn *, Posn *));
  41. static    void    op_change P((int, int, long, Posn *, Posn *));
  42. static    void    op_yank P((Posn *, Posn *));
  43. static    bool_t    dojoin P((void));
  44.  
  45. /*
  46.  * Command type table. This is used for certain operations which are
  47.  * the same for "classes" of commands, e.g. for disallowing their use
  48.  * as targets of operators.
  49.  *
  50.  * Entries in this table having value 0 are unimplemented commands.
  51.  *
  52.  * If TARGET is set, the command may be used as the target for one of
  53.  * the operators (e.g. 'c'); the default is that targets are character-
  54.  * based unless TGT_LINE is set in which case they are line-based.
  55.  * Similarly, the default is that targets are exclusive, unless the
  56.  * TGT_INCLUSIVE flag is set.
  57.  *
  58.  * Q: WHAT DO WE DO ABOUT RETURN and LINEFEED???
  59.  * A: The ascii_map() macro (see ascii.h) handles this for QNX (I think).
  60.  */
  61. #define        COMMAND        0x1        /* is implemented */
  62. #define        TARGET        0x2        /* can serve as a target */
  63. #define        TGT_LINE    0x4        /* a line-based target */
  64. #define        TGT_CHAR    0        /* a char-based target */
  65. #define        TGT_INCLUSIVE    0x8        /* an inclusive target */
  66. #define        TGT_EXCLUSIVE    0        /* an exclusive target */
  67. #define        TWO_CHAR    0x10        /* a two-character command */
  68.  
  69. static    struct {
  70.     bool_t        (*c_func) P((int, int));
  71.     unsigned char    c_flags;
  72. } cmd_types[256] = {
  73.  /* ^@ */    do_badcmd,    0,
  74.  /* ^A */    do_badcmd,    0,
  75.  /* ^B */    do_page,    COMMAND,
  76.  /* ^C */    do_badcmd,    0,
  77.  /* ^D */    do_scroll,    COMMAND,
  78.  /* ^E */    do_scroll,    COMMAND,
  79.  /* ^F */    do_page,    COMMAND,
  80.  /* ^G */    do_cmd,        COMMAND,
  81.  /* ^H */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  82.  /* ^I */    do_badcmd,    0,
  83.  /* ^J */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  84.  /* ^K */    do_badcmd,    0,
  85.  /* ^L */    do_cmd,        COMMAND,
  86.  /* ^M */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  87.  /* ^N */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  88.  /* ^O */    do_cmd,        COMMAND,
  89.  /* ^P */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  90.  /* ^Q */    do_badcmd,    0,
  91.  /* ^R */    do_cmd,        COMMAND,
  92.  /* ^S */    do_badcmd,    0,
  93.  /* ^T */    do_cmd,        COMMAND,
  94.  /* ^U */    do_scroll,    COMMAND,
  95.  /* ^V */    do_badcmd,    0,
  96.  /* ^W */    do_cmd,        COMMAND,
  97.  /* ^X */    do_badcmd,    0,
  98.  /* ^Y */    do_scroll,    COMMAND,
  99.  /* ^Z */    do_cmd,        COMMAND,
  100.  /* ESCAPE */    do_cmd,        COMMAND,
  101.  /* ^\ */    do_badcmd,    0,
  102.  /* ^] */    do_cmd,        COMMAND,
  103.  /* ^^ */    do_cmd,        COMMAND,
  104.  /* ^_ */    do_rchar,    COMMAND,
  105.  /* space */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  106.  /* ! */    do_cmd,        COMMAND,
  107.  /* " */    do_cmd,        COMMAND | TWO_CHAR,
  108.  /* # */    do_badcmd,    0,
  109.  /* $ */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
  110.  /* % */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
  111.  /* & */    do_cmd,        COMMAND,
  112.  /* ' */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
  113.  /* ( */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  114.  /* ) */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  115.  /* * */    do_badcmd,    0,
  116.  /* + */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  117.  /* , */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  118.  /* - */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  119.  /* . */    do_cmd,        COMMAND,
  120.  /* / */    do_cmd,        COMMAND,
  121.  /* 0 */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  122.  /* 1 */    do_badcmd,    0,
  123.  /* 2 */    do_badcmd,    0,
  124.  /* 3 */    do_badcmd,    0,
  125.  /* 4 */    do_badcmd,    0,
  126.  /* 5 */    do_badcmd,    0,
  127.  /* 6 */    do_badcmd,    0,
  128.  /* 7 */    do_badcmd,    0,
  129.  /* 8 */    do_badcmd,    0,
  130.  /* 9 */    do_badcmd,    0,
  131.  /* : */    do_cmd,        COMMAND,
  132.  /* ; */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  133.  /* < */    do_cmd,        COMMAND,
  134.  /* = */    do_badcmd,    0,
  135.  /* > */    do_cmd,        COMMAND,
  136.  /* ? */    do_cmd,        COMMAND,
  137.  /* @ */    do_cmd,        COMMAND | TWO_CHAR,
  138.  /* A */    do_ins,        COMMAND,
  139.  /* B */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  140.  /* C */    do_cmd,        COMMAND,
  141.  /* D */    do_cmd,        COMMAND,
  142.  /* E */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
  143.  /* F */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
  144.  /* G */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  145.  /* H */    do_HLM,        COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  146.  /* I */    do_ins,        COMMAND,
  147.  /* J */    do_cmd,        COMMAND,
  148.  /* K */    do_badcmd,    0,
  149.  /* L */    do_HLM,        COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  150.  /* M */    do_HLM,        COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  151.  /* N */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  152.  /* O */    do_ins,        COMMAND,
  153.  /* P */    do_cmd,        COMMAND,
  154.  /* Q */    do_badcmd,    0,
  155.  /* R */    do_cmd,        COMMAND,
  156.  /* S */    do_cmd,        COMMAND,
  157.  /* T */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
  158.  /* U */    do_badcmd,    0,
  159.  /* V */    do_badcmd,    0,
  160.  /* W */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  161.  /* X */    do_x,        COMMAND,
  162.  /* Y */    do_cmd,        COMMAND,
  163.  /* Z */    do_cmd,        COMMAND | TWO_CHAR,
  164.  /* [ */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
  165.  /* \ */    do_badcmd,    0,
  166.  /* ] */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
  167.  /* ^ */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  168.  /* _ */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  169.  /* ` */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
  170.  /* a */    do_ins,        COMMAND,
  171.  /* b */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  172.  /* c */    do_cmd,        COMMAND,
  173.  /* d */    do_cmd,        COMMAND,
  174.  /* e */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
  175.  /* f */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE | TWO_CHAR,
  176.  /* g */    do_cmd,        COMMAND,
  177.  /* h */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  178.  /* i */    do_ins,        COMMAND,
  179.  /* j */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  180.  /* k */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  181.  /* l */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  182.  /* m */    do_cmd,        COMMAND | TWO_CHAR,
  183.  /* n */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  184.  /* o */    do_ins,        COMMAND,
  185.  /* p */    do_cmd,        COMMAND,
  186.  /* q */    do_badcmd,    0,
  187.  /* r */    do_cmd,        COMMAND,
  188.  /* s */    do_cmd,        COMMAND,
  189.  /* t */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE | TWO_CHAR,
  190.  /* u */    do_cmd,        COMMAND,
  191.  /* v */    do_badcmd,    0,
  192.  /* w */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  193.  /* x */    do_x,        COMMAND,
  194.  /* y */    do_cmd,        COMMAND,
  195.  /* z */    do_z,        COMMAND | TWO_CHAR,
  196.  /* { */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  197.  /* | */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  198.  /* } */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  199.  /* ~ */    do_rchar,    COMMAND,
  200.  /* DEL */    do_badcmd,    0,
  201.  /* K_HELP */    do_cmd,        COMMAND,
  202.  /* K_UNDO */    do_cmd,        COMMAND,
  203.  /* K_INSERT */    do_ins,        COMMAND,
  204.  /* K_HOME */    do_HLM,        COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  205.  /* K_UARROW */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  206.  /* K_DARROW */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  207.  /* K_LARROW */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  208.  /* K_RARROW */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  209.  /* K_CGRAVE */    do_cmd,        COMMAND,
  210. };
  211.  
  212. #define    NOP        '\0'        /* no pending operation */
  213.  
  214. static    int    operator = NOP;        /* current pending operator */
  215.  
  216. /*
  217.  * When a cursor motion command is made, it is marked as being a character
  218.  * or line oriented motion. Then, if an operator is in effect, the operation
  219.  * becomes character or line oriented accordingly.
  220.  *
  221.  * Character motions are marked as being inclusive or not. Most char.
  222.  * motions are inclusive, but some (e.g. 'w') are not.
  223.  */
  224.  
  225. static    enum {
  226.     m_bad,        /* 'bad' motion type marks unusable yank buf */
  227.     m_nonincl,        /* non-inclusive character motion */
  228.     m_incl,        /* inclusive character motion */
  229.     m_line        /* line-based motion */
  230. } mtype;        /* type of the current cursor motion */
  231.  
  232. /*
  233.  * Cursor position at start of operator.
  234.  */
  235. static    Posn    startop;
  236.  
  237. /*
  238.  * Operators can have counts either before the operator, or between the
  239.  * operator and the following cursor motion as in:
  240.  *
  241.  *    d3w or 3dw
  242.  *
  243.  * The number is initially stored in Prenum as it is processed.
  244.  *
  245.  * If a count is given before an operator, it is saved in opnum when the
  246.  * initial recognition of the operator takes place. If normal() is called
  247.  * with a pending operator, the count in opnum (if present) overrides
  248.  * any count that comes later.
  249.  */
  250. static    long    Prenum;
  251. static    long    opnum;
  252.  
  253. /*
  254.  * This variable contains the name of the yank/put buffer we are
  255.  * currently using. It is set by the " command. The default value
  256.  * is always '@'; other values are a-z and possibly ':'.
  257.  */
  258. static    int    cur_yp_name = '@';
  259.  
  260. /*
  261.  * This state variable is TRUE if we got a preceding buffer
  262.  * name: if set, the buffer_name variable contains the letter
  263.  * which was given as the buffer name.
  264.  */
  265. static    bool_t    got_name = FALSE;
  266. static    char    buffer_name;
  267.  
  268. /*
  269.  * Return value of Prenum unless it is 0, in which case return the
  270.  * default value of 1.
  271.  */
  272. #define    LDEF1PRENUM    ((Prenum == 0 ? 1L : Prenum))
  273.  
  274. /*
  275.  * Return value of Prenum as an int, unless it is 0, in which case
  276.  * return the default value of 1.
  277.  *
  278.  * Note that this assumes that Prenum will never be negative.
  279.  */
  280. #define    IDEF1PRENUM    (Prenum == 0 ? \
  281.              1 : \
  282.              sizeof (int) == sizeof (long) || \
  283.                Prenum <= INT_MAX ? \
  284.              (int) Prenum : \
  285.              INT_MAX)
  286.  
  287. /*
  288.  * Redo buffer.
  289.  */
  290. struct redo {
  291.     enum {
  292.         r_insert,
  293.     r_replace1,
  294.     r_normal
  295.     }        r_mode;
  296.     Flexbuf    r_fb;
  297. } Redo;
  298.  
  299. /*
  300.  * Execute a command in "normal" (i.e. command) mode.
  301.  */
  302. bool_t
  303. normal(c)
  304. register int    c;
  305. {
  306.     /*
  307.      * This variable is used to recall whether we got
  308.      * an operator last time, to decide whether we
  309.      * should apply it this time.
  310.      */
  311.     register bool_t    finish_op;
  312.  
  313.     /*
  314.      * TRUE if we are awaiting a second character to finish the
  315.      * current command - this is for two-character commands like
  316.      * ZZ, and for commands taking a single character argument.
  317.      * The "first_char" and "second_char" variables are for the
  318.      * first character (stored between calls), and the second one.
  319.      */
  320.     static bool_t    two_char = FALSE;
  321.     static int        first_char;
  322.     int            second_char;
  323.     unsigned char    cflags;
  324.     bool_t        (*cfunc) P((int, int)) = NULL;
  325.  
  326.  
  327.     /*
  328.      * If the character is a digit, and it is not a leading '0',
  329.      * compute Prenum instead of doing a command.  Leading zeroes
  330.      * are treated specially because '0' is a valid command.
  331.      *
  332.      * If two_char is set, don't treat digits this way; they are
  333.      * passed in as the second character. This is because none of
  334.      * the two-character commands are allowed to take prenums in
  335.      * the middle; you want a prenum, you have to type it before
  336.      * the command. So "t3" works as you might expect it to.
  337.      */
  338.     if (!two_char && is_digit(c) && (c != '0' || Prenum > 0)) {
  339.     Prenum = Prenum * 10 + (c - '0');
  340.     return(FALSE);
  341.     }
  342.  
  343.     /*
  344.      * If there is an operator pending, then the command we take
  345.      * this time will terminate it.  Finish_op tells us to finish
  346.      * the operation before returning this time (unless it was
  347.      * cancelled).
  348.      */
  349.     finish_op = (operator != NOP);
  350.  
  351.     /*
  352.      * If we're in the middle of an operator AND we had a count before
  353.      * the operator, then that count overrides the current value of
  354.      * Prenum. What this means effectively, is that commands like
  355.      * "3dw" get turned into "d3w" which makes things fall into place
  356.      * pretty neatly.
  357.      */
  358.     if (finish_op || two_char) {
  359.     if (opnum != 0) {
  360.         Prenum = opnum;
  361.     }
  362.     } else {
  363.     opnum = 0;
  364.     }
  365.  
  366.     /*
  367.      * If we got given a buffer name last time around, it is only
  368.      * good for one operation; so at the start of each new command
  369.      * we set or clear the yankput module's idea of the buffer name.
  370.      * We don't do this if finish_op is set because it is not the
  371.      * start of a new command.
  372.      */
  373.     if (!finish_op) {
  374.     cur_yp_name = got_name ? buffer_name : '@';
  375.     got_name = FALSE;
  376.     }
  377.  
  378.     if (c > (sizeof(cmd_types) / sizeof(cmd_types[0]) - 1)) {
  379.     operator = NOP;
  380.     Prenum = 0;
  381.         beep(curwin);
  382.     return(FALSE);
  383.     }
  384.  
  385.     /*
  386.      * If two_char is set, it means we got the first character of
  387.      * a two-character command last time. So check the second char,
  388.      * and set cflags appropriately.
  389.      */
  390.     if (two_char) {
  391.     second_char = c;
  392.     two_char = FALSE;
  393.     cflags = cmd_types[ascii_map(first_char)].c_flags;
  394.     cfunc = cmd_types[ascii_map(first_char)].c_func;
  395.  
  396.     /*
  397.      * This seems to be a universal rule - if a two-character
  398.      * command has ESC as the second character, it means "abort".
  399.      */
  400.     if (second_char == ESC) {
  401.         operator = NOP;
  402.         finish_op = FALSE;
  403.         Prenum = 0;
  404.         return(FALSE);
  405.     }
  406.  
  407.     } else {
  408.     /*
  409.      * Received a command. Find out its characteristics ...
  410.      */
  411.     first_char = c;
  412.     second_char = '\0';
  413.     cflags = cmd_types[ascii_map(first_char)].c_flags;
  414.     cfunc = cmd_types[ascii_map(first_char)].c_func;
  415.  
  416.     /*
  417.      * It's a two-character command. So wait until we get
  418.      * the second character before proceeding.
  419.      */
  420.     if (cflags & TWO_CHAR) {
  421.         two_char = TRUE;
  422.         first_char = c;
  423.         if (Prenum != 0) {
  424.         opnum = Prenum;
  425.         Prenum = 0;
  426.         }
  427.         return(FALSE);
  428.     }
  429.  
  430.     /*
  431.      * If we got an operator last time, and the user
  432.      * typed the same character again, we fake out the
  433.      * default "apply to this line" rule by changing
  434.      * the input character to a '_' which means "the
  435.      * current line."
  436.      */
  437.     if (finish_op) {
  438.         if (operator == c) {
  439.         first_char = c = '_';
  440.         cflags = cmd_types[ascii_map(c)].c_flags;
  441.         cfunc = cmd_types[ascii_map(c)].c_func;
  442.         } else if (!(cflags & TARGET)) {
  443.         beep(curwin);
  444.         operator = NOP;
  445.         Prenum = 0;
  446.         return(FALSE);
  447.         }
  448.     }
  449.     }
  450.  
  451.     /*
  452.      * At this point, cfunc must be set - if not, the entry in the
  453.      * command table is zero, so disallow the input character.
  454.      */
  455.     if (cfunc == NULL) {
  456.     operator = NOP;
  457.     Prenum = 0;
  458.         beep(curwin);
  459.     return(FALSE);
  460.     }
  461.  
  462.     if (cflags & TARGET) {
  463.  
  464.     /*
  465.      * A cursor movement command.
  466.      */
  467.  
  468.     if (cflags & TGT_LINE) {
  469.         mtype = m_line;
  470.     } else {
  471.         if (cflags & TGT_INCLUSIVE) {
  472.         mtype = m_incl;
  473.         } else {
  474.         mtype = m_nonincl;
  475.         }
  476.     }
  477.  
  478.     if (!(*cfunc)(first_char, second_char)) {
  479.         beep(curwin);
  480.         operator = NOP;
  481.         Prenum = 0;
  482.         return(FALSE);
  483.     }
  484.  
  485.     /*
  486.      * If an operation is pending, handle it...
  487.      */
  488.     if (finish_op) {
  489.         Posn    top, bot;
  490.  
  491.         top = startop;
  492.         bot = *curwin->w_cursor;
  493.  
  494.         /*
  495.          * Put the cursor back to its starting position.
  496.          */
  497.         move_cursor(curwin, startop.p_line, startop.p_index);
  498.  
  499.         if (lt(&bot, &top)) {
  500.         pswap(&top, &bot);
  501.         }
  502.  
  503.         switch (operator) {
  504.         case '<':
  505.         case '>':
  506.         op_shift(operator, first_char, second_char, Prenum, &top, &bot);
  507.         break;
  508.  
  509.         case 'd':
  510.         op_delete(first_char, second_char, Prenum, &top, &bot);
  511.         break;
  512.  
  513.         case 'y':
  514.         op_yank(&top, &bot);
  515.         break;
  516.  
  517.         case 'c':
  518.         op_change(first_char, second_char, Prenum, &top, &bot);
  519.         break;
  520.  
  521.         case '!':
  522.         specify_pipe_range(curwin, top.p_line, bot.p_line);
  523.         cmd_init(curwin, '!');
  524.         break;
  525.  
  526.         default:
  527.         beep(curwin);
  528.         }
  529.         operator = NOP;
  530.     }
  531.     } else {
  532.     /*
  533.      * A command that does something.
  534.      * Since it isn't a target, no operators need apply.
  535.      */
  536.     if (finish_op) {
  537.         beep(curwin);
  538.         operator = NOP;
  539.     }
  540.  
  541.     (void) (*cfunc)(first_char, second_char);
  542.     }
  543.  
  544.     Prenum = 0;
  545.     return(TRUE);
  546. }
  547.  
  548. /*
  549.  * Handle cursor movement commands.
  550.  * These are used simply to move the cursor somewhere,
  551.  * and also as targets for the various operators.
  552.  *
  553.  * If the return value is FALSE, the caller will complain
  554.  * loudly to the user and cancel any outstanding operator.
  555.  * The cursor should hopefully not have moved.
  556.  *
  557.  * Arguments are the first and second characters (where appropriate).
  558.  */
  559. static bool_t
  560. do_target(c1, c2)
  561. int    c1, c2;
  562. {
  563.     bool_t        skip_spaces = FALSE;
  564.     bool_t        retval = TRUE;
  565.  
  566.     switch (c1) {
  567.     case 'G':
  568.     setpcmark(curwin);
  569.     do_goto((Prenum > 0) ? Prenum : MAX_LINENO);
  570.     skip_spaces = TRUE;
  571.     break;
  572.  
  573.     case 'l':
  574.     case ' ':
  575.     c1 = K_RARROW;
  576.     /* fall through ... */
  577.     case K_RARROW:
  578.     case 'h':
  579.     case K_LARROW:
  580.     case CTRL('H'):
  581.     {
  582.     register bool_t (*mvfunc) P((Xviwin *, bool_t));
  583.     register long    n;
  584.     register long    i;
  585.  
  586.     if (c1 == K_RARROW) {
  587.         mvfunc = one_right;
  588.     } else {
  589.         mvfunc = one_left;
  590.     }
  591.  
  592.     n = LDEF1PRENUM;
  593.     for (i = 0; i < n; i++) {
  594.         if (!(*mvfunc)(curwin, FALSE)) {
  595.         break;
  596.         }
  597.     }
  598.     if (i == 0) {
  599.         retval = FALSE;
  600.     } else {
  601.         curwin->w_set_want_col = TRUE;
  602.     }
  603.     break;
  604.     }
  605.  
  606.     case '-':
  607.     skip_spaces = TRUE;
  608.     /* FALL THROUGH */
  609.     case 'k':
  610.     case K_UARROW:
  611.     case CTRL('P'):
  612.     if (!oneup(curwin, LDEF1PRENUM)) {
  613.         retval = FALSE;
  614.     }
  615.     break;
  616.  
  617.     case '+':
  618.     case '\r':
  619.     skip_spaces = TRUE;
  620.     /* FALL THROUGH */
  621.     case '\n':
  622.     case 'j':
  623.     case K_DARROW:
  624.     case CTRL('N'):
  625.     if (!onedown(curwin, LDEF1PRENUM)) {
  626.         retval = FALSE;
  627.     }
  628.     break;
  629.  
  630.     /*
  631.      * This is a strange motion command that helps make
  632.      * operators more logical. It is actually implemented,
  633.      * but not documented in the real 'vi'. This motion
  634.      * command actually refers to "the current line".
  635.      * Commands like "dd" and "yy" are really an alternate
  636.      * form of "d_" and "y_". It does accept a count, so
  637.      * "d3_" works to delete 3 lines.
  638.      */
  639.     case '_':
  640.     (void) onedown(curwin, LDEF1PRENUM - 1);
  641.     break;
  642.  
  643.     case '|':
  644.     begin_line(curwin, FALSE);
  645.  
  646.     if (Prenum > 0) {
  647.         coladvance(curwin, LONG2INT(Prenum - 1));
  648.     }
  649.     curwin->w_curswant = Prenum - 1;
  650.     break;
  651.  
  652.     case '%':
  653.     {
  654.         Posn    *pos = showmatch();
  655.  
  656.         if (pos == NULL) {
  657.         retval = FALSE;
  658.         } else {
  659.         setpcmark(curwin);
  660.         move_cursor(curwin, pos->p_line, pos->p_index);
  661.         curwin->w_set_want_col = TRUE;
  662.         }
  663.     }
  664.     break;
  665.  
  666.     case '$':
  667.     while (one_right(curwin, FALSE))
  668.         ;
  669.     curwin->w_curswant = INT_MAX;
  670.         /* so we stay at the end ... */
  671.     curwin->w_set_want_col = FALSE;
  672.     break;
  673.  
  674.     case '^':
  675.     case '0':
  676.     begin_line(curwin, c1 == '^');
  677.     break;
  678.  
  679.     case 'n':
  680.     case 'N':
  681.     curwin->w_set_want_col = TRUE;
  682.     (void) dosearch(curwin, "", c1);
  683.     break;
  684.  
  685.     case '(':
  686.     case ')':
  687.     case '{':
  688.     case '}':
  689.     case '[':
  690.     case ']':
  691.     {
  692.         int    dir = FORWARD;
  693.     char    *pattern;
  694.     Posn    *newpos;
  695.  
  696.     switch (c1) {
  697.     case '(':
  698.         dir = BACKWARD;
  699.         /*FALLTHROUGH*/
  700.     case ')':
  701.         pattern = Ps(P_sentences);
  702.         break;
  703.  
  704.     case '{':
  705.         dir = BACKWARD;
  706.         /*FALLTHROUGH*/
  707.     case '}':
  708.         pattern = Ps(P_paragraphs);
  709.         break;
  710.  
  711.     case '[':
  712.         dir = BACKWARD;
  713.         /*FALLTHROUGH*/
  714.     case ']':
  715.         if (c1 != c2) {
  716.         retval = FALSE;
  717.         } else {
  718.         pattern = Ps(P_sections);
  719.         }
  720.     }
  721.     if (retval) {
  722.         curwin->w_set_want_col = TRUE;
  723.         newpos = find_pattern(pattern, dir, IDEF1PRENUM);
  724.         if (newpos != NULL) {
  725.         setpcmark(curwin);
  726.         move_cursor(curwin, newpos->p_line, newpos->p_index);
  727.         } else {
  728.         retval = FALSE;
  729.         }
  730.     }
  731.     break;
  732.     }
  733.  
  734.     case '\'':
  735.     case '`':
  736.     {
  737.     Posn    *mark;
  738.  
  739.     mark = getmark(c2, curbuf);
  740.     if (mark == NULL) {
  741.         retval = FALSE;
  742.     } else {
  743.         Posn    dest;
  744.  
  745.         /*
  746.          * Record posn before re-setting the mark -
  747.          * so that we don't accidentally side-effect
  748.          * the place we are moving to! What a hack.
  749.          */
  750.         dest = *mark;
  751.  
  752.         setpcmark(curwin);
  753.  
  754.         move_cursor(curwin, dest.p_line, c1 == '`' ? dest.p_index : 0);
  755.         if (c1 == '`') {
  756.         mtype = m_nonincl;
  757.         } else {
  758.         skip_spaces = TRUE;
  759.         }
  760.     }
  761.     break;
  762.     }
  763.     }
  764.  
  765.     if (retval && skip_spaces) {
  766.     begin_line(curwin, TRUE);
  767.     }
  768.  
  769.     return(retval);
  770. }
  771.  
  772. static bool_t
  773. do_cmd(c1, c2)
  774. int    c1, c2;
  775. {
  776.     switch (c1) {
  777.     case K_HELP:
  778.     do_help(curwin);
  779.     break;
  780.  
  781.     case CTRL('R'):
  782.     case CTRL('L'):
  783.     redraw_screen();
  784.     break;
  785.  
  786.     case CTRL('G'):
  787.     show_file_info(curwin);
  788.     break;
  789.  
  790.     case CTRL(']'):        /* :ta to current identifier */
  791.     tagword();
  792.     break;
  793.  
  794.     /*
  795.      * Some convenient abbreviations...
  796.      */
  797.     case 'D':
  798.     stuff("\"%cd$", cur_yp_name);
  799.     break;
  800.  
  801.     case 'Y':
  802.     stuff("\"%c%dyy", cur_yp_name, IDEF1PRENUM);
  803.     break;
  804.  
  805.     case 'C':
  806.     stuff("\"%cc$", cur_yp_name);
  807.     break;
  808.  
  809.     case 'S':
  810.         stuff("\"%c%dcc", cur_yp_name, IDEF1PRENUM);
  811.     break;
  812.  
  813.     /*
  814.      * Operators.
  815.      */
  816.     case 'd':
  817.     case 'c':
  818.     case 'y':
  819.     case '>':
  820.     case '<':
  821.     case '!':
  822.     if (Prenum != 0)
  823.         opnum = Prenum;
  824.     startop = *curwin->w_cursor;
  825.     operator = c1;
  826.     break;
  827.  
  828.     case 'p':
  829.     case 'P':
  830.     Redo.r_mode = r_normal;
  831.     do_put(curwin, curwin->w_cursor, (c1 == 'p') ? FORWARD : BACKWARD,
  832.                             cur_yp_name);
  833.     if (is_digit(cur_yp_name) && cur_yp_name != '0' && cur_yp_name != '9') {
  834.         cur_yp_name++;
  835.     }
  836.     flexclear(&Redo.r_fb);
  837.     (void) lformat(&Redo.r_fb, "\"%c%d%c", cur_yp_name, IDEF1PRENUM, c1);
  838.     break;
  839.  
  840.     case 's':        /* substitute characters */
  841.     start_command(curwin);
  842.     replchars(curwin, curwin->w_cursor->p_line,
  843.                 curwin->w_cursor->p_index, IDEF1PRENUM, "");
  844.     updateline(curwin);
  845.     Redo.r_mode = r_insert;
  846.     flexclear(&Redo.r_fb);
  847.     (void) lformat(&Redo.r_fb, "%lds", IDEF1PRENUM);
  848.     startinsert(FALSE);
  849.     break;
  850.  
  851.     case ':':
  852.     case '?':
  853.     case '/':
  854.     cmd_init(curwin, c1);
  855.     break;
  856.  
  857.     case '&':
  858.     (void) do_ampersand(curwin, curwin->w_cursor->p_line,
  859.                     curwin->w_cursor->p_line, "");
  860.     begin_line(curwin, TRUE);
  861.     updateline(curwin);
  862.     break;
  863.  
  864.     case 'R':
  865.     case 'r':
  866.     Redo.r_mode = (c1 == 'r') ? r_replace1 : r_insert;
  867.     flexclear(&Redo.r_fb);
  868.     flexaddch(&Redo.r_fb, c1);
  869.     startreplace(c1);
  870.     break;
  871.  
  872.     case 'J':
  873.     if (!dojoin())
  874.         beep(curwin);
  875.  
  876.     Redo.r_mode = r_normal;
  877.     flexclear(&Redo.r_fb);
  878.     flexaddch(&Redo.r_fb, c1);
  879.     update_buffer(curbuf);
  880.     break;
  881.  
  882.     case K_CGRAVE:            /* shorthand command */
  883. #ifndef    QNX
  884.     /*
  885.      * We can't use this key on QNX.
  886.      */
  887.     case CTRL('^'):
  888. #endif
  889.     do_alt_edit(curwin);
  890.     break;
  891.  
  892.     case 'u':
  893.     case K_UNDO:
  894.     undo(curwin);
  895.     break;
  896.  
  897.     case CTRL('Z'):            /* suspend editor */
  898.     do_suspend(curwin);
  899.     break;
  900.  
  901.     /*
  902.      * Buffer handling.
  903.      */
  904.     case CTRL('T'):            /* shrink window */
  905.     resize_window(curwin, - IDEF1PRENUM);
  906.     move_cursor_to_window(curwin);
  907.     break;
  908.  
  909.     case CTRL('W'):            /* grow window */
  910.     resize_window(curwin, IDEF1PRENUM);
  911.     break;
  912.  
  913.     case CTRL('O'):    /* make window as large as possible */
  914.     resize_window(curwin, INT_MAX);
  915.     break;
  916.  
  917.     case 'g':
  918.     /*
  919.      * Find the next window that the cursor
  920.      * can be displayed in; i.e. at least one
  921.      * text row is displayed.
  922.      */
  923.     do {
  924.         curwin = next_window(curwin);
  925.     } while (curwin->w_nrows < 2);
  926.     curbuf = curwin->w_buffer;
  927.     move_cursor_to_window(curwin);
  928.     wind_goto(curwin);
  929.     break;
  930.  
  931.     case '"':
  932.     got_name = TRUE;
  933.     buffer_name = c2;
  934.     break;
  935.  
  936.     case '@':
  937.     yp_stuff_input(curwin, c2, TRUE);
  938.     break;
  939.  
  940.     /*
  941.      * Marks
  942.      */
  943.     case 'm':
  944.     if (!setmark(c2, curbuf, curwin->w_cursor))
  945.         beep(curwin);
  946.     break;
  947.  
  948.     case 'Z':        /* write, if changed, and exit */
  949.     if (c2 != 'Z') {
  950.         beep(curwin);
  951.         break;
  952.     }
  953.  
  954.     /*
  955.      * Make like a ":x" command.
  956.      */
  957.     do_xit(curwin);
  958.     break;
  959.  
  960.     case '.':
  961.     /*
  962.      * '.', meaning redo. As opposed to '.' as a target.
  963.      */
  964.     stuff("%s", flexgetstr(&Redo.r_fb));
  965.     if (Redo.r_mode != r_normal) {
  966.         yp_stuff_input(curwin, '<', TRUE);
  967.         if (Redo.r_mode == r_insert) {
  968.         stuff("%c", ESC);
  969.         }
  970.     }
  971.     break;
  972.  
  973.     default:
  974.     beep(curwin);
  975.     break;
  976.     }
  977.  
  978.     return(FALSE);
  979. }
  980.  
  981. static bool_t
  982. do_badcmd(c1, c2)
  983. int    c1, c2;
  984. {
  985.     beep(curwin);
  986.     return(FALSE);
  987. }
  988.  
  989. /*
  990.  * Handle page motion (control-F or control-B).
  991.  */
  992. static bool_t
  993. do_page(c1, c2)
  994. register int    c1, c2;
  995. {
  996.     long        overlap;
  997.     long        n;
  998.  
  999.     /*
  1000.      * First move the cursor to the top of the screen
  1001.      * (for ^B), or to the top of the next screen (for ^F).
  1002.      */
  1003.     move_cursor(curwin, (c1 == CTRL('B')) ?
  1004.            curwin->w_topline : curwin->w_botline, 0);
  1005.  
  1006.     /*
  1007.      * Cursor could have moved to the lastline of the buffer,
  1008.      * if the window is at the end of the buffer. Disallow
  1009.      * the cursor from being outside the buffer's bounds.
  1010.      */
  1011.     if (curwin->w_cursor->p_line == curbuf->b_lastline) {
  1012.     move_cursor(curwin, curbuf->b_lastline->l_prev, 0);
  1013.     }
  1014.  
  1015.     /*
  1016.      * Decide on the amount of overlap to use.
  1017.      */
  1018.     if (curwin->w_nrows > 10) {
  1019.     /*
  1020.      * At least 10 text lines in window.
  1021.      */
  1022.     overlap = 2;
  1023.     } else if (curwin->w_nrows > 3) {
  1024.     /*
  1025.      * Between 3 and 9 text lines in window.
  1026.      */
  1027.     overlap = 1;
  1028.     } else {
  1029.     /*
  1030.      * 1 or 2 text lines in window.
  1031.      */
  1032.     overlap = 0;
  1033.     }
  1034.  
  1035.     /*
  1036.      * Given the overlap, decide where to move the cursor;
  1037.      * this will determine the new top line of the screen.
  1038.      */
  1039.     if (c1 == CTRL('F')) {
  1040.     n = - overlap;
  1041.     n += (LDEF1PRENUM - 1) * (curwin->w_nrows - overlap - 1);
  1042.     } else {
  1043.     n = (- LDEF1PRENUM) * (curwin->w_nrows - overlap - 1);
  1044.     }
  1045.  
  1046.     if (n > 0) {
  1047.     (void) onedown(curwin, n);
  1048.     } else {
  1049.     (void) oneup(curwin, -n);
  1050.     }
  1051.  
  1052.     /*
  1053.      * Redraw the screen with the cursor at the top.
  1054.      */
  1055.     begin_line(curwin, TRUE);
  1056.     curwin->w_topline = curwin->w_cursor->p_line;
  1057.     update_window(curwin);
  1058.  
  1059.     if (c1 == CTRL('B')) {
  1060.     /*
  1061.      * And move it to the bottom.
  1062.      */
  1063.     move_window_to_cursor(curwin);
  1064.     cursupdate(curwin);
  1065.     move_cursor(curwin, curwin->w_botline->l_prev, 0);
  1066.     begin_line(curwin, TRUE);
  1067.     }
  1068.  
  1069.     /*
  1070.      * Finally, show where we are in the file.
  1071.      */
  1072.     show_file_info(curwin);
  1073.  
  1074.     return(FALSE);
  1075. }
  1076.  
  1077. static bool_t
  1078. do_scroll(c1, c2)
  1079. int    c1, c2;
  1080. {
  1081.     switch (c1) {
  1082.     case CTRL('D'):
  1083.     scrollup(curwin, curwin->w_nrows / 2);
  1084.     (void) onedown(curwin, (long) (curwin->w_nrows / 2));
  1085.     break;
  1086.  
  1087.     case CTRL('U'):
  1088.     scrolldown(curwin, curwin->w_nrows / 2);
  1089.     (void) oneup(curwin, (long) (curwin->w_nrows / 2));
  1090.     break;
  1091.  
  1092.     case CTRL('E'):
  1093.     scrollup(curwin, (unsigned) IDEF1PRENUM);
  1094.     break;
  1095.  
  1096.     case CTRL('Y'):
  1097.     scrolldown(curwin, (unsigned) IDEF1PRENUM);
  1098.     break;
  1099.     }
  1100.  
  1101.     update_window(curwin);
  1102.     move_cursor_to_window(curwin);
  1103.     return(FALSE);
  1104. }
  1105.  
  1106. /*
  1107.  * Handle word motion ('w', 'W', 'b', 'B', 'e' or 'E').
  1108.  */
  1109. static bool_t
  1110. do_word(c1, c2)
  1111. register int    c1, c2;
  1112. {
  1113.     register Posn    *(*func) P((Posn *, int, bool_t));
  1114.     register long    n;
  1115.     register int    lc;
  1116.     register int    type;
  1117.     Posn        pos;
  1118.  
  1119.     if (is_upper(c1)) {
  1120.     type = 1;
  1121.     lc = to_lower(c1);
  1122.     } else {
  1123.     type = 0;
  1124.     lc = c1;
  1125.     }
  1126.     curwin->w_set_want_col = TRUE;
  1127.  
  1128.     switch (lc) {
  1129.     case 'b':
  1130.     func = bck_word;
  1131.     break;
  1132.  
  1133.     case 'w':
  1134.     func = fwd_word;
  1135.     break;
  1136.  
  1137.     case 'e':
  1138.     func = end_word;
  1139.     break;
  1140.     }
  1141.  
  1142.     pos = *curwin->w_cursor;
  1143.  
  1144.     for (n = LDEF1PRENUM; n > 0; n--) {
  1145.     Posn    *newpos;
  1146.     bool_t    skip_whites;
  1147.  
  1148.     /*
  1149.      * "cw" is a special case; the whitespace after
  1150.      * the end of the last word involved in the change
  1151.      * does not get changed. The following code copes
  1152.      * with this strangeness.
  1153.      */
  1154.     if (n == 1 && operator == 'c' && lc == 'w') {
  1155.         skip_whites = FALSE;
  1156.         mtype = m_incl;
  1157.     } else {
  1158.         skip_whites = TRUE;
  1159.     }
  1160.  
  1161.     newpos = (*func)(&pos, type, skip_whites);
  1162.  
  1163.     if (newpos == NULL) {
  1164.         return(FALSE);
  1165.     }
  1166.  
  1167.     if (n == 1 && lc == 'w' && operator != NOP &&
  1168.                     newpos->p_line != pos.p_line) {
  1169.         /*
  1170.          * We are on the last word to be operated
  1171.          * upon, and have crossed the line boundary.
  1172.          * This should not happen, so back up to
  1173.          * the end of the line the word is on.
  1174.          */
  1175.         while (dec(newpos) == mv_SAMELINE)
  1176.         ;
  1177.         mtype = m_incl;
  1178.     }
  1179.  
  1180.     if (skip_whites == FALSE) {
  1181.         (void) dec(newpos);
  1182.     }
  1183.     pos = *newpos;
  1184.     }
  1185.     move_cursor(curwin, pos.p_line, pos.p_index);
  1186.     return(TRUE);
  1187. }
  1188.  
  1189. static bool_t
  1190. do_csearch(c1, c2)
  1191. int    c1, c2;
  1192. {
  1193.     bool_t    retval = TRUE;
  1194.     Posn    *pos;
  1195.     int    dir;
  1196.  
  1197.     switch (c1) {
  1198.     case 'T':
  1199.     case 't':
  1200.     case 'F':
  1201.     case 'f':
  1202.     if (is_upper(c1)) {
  1203.         dir = BACKWARD;
  1204.         c1 = to_lower(c1);
  1205.     } else {
  1206.         dir = FORWARD;
  1207.     }
  1208.  
  1209.     curwin->w_set_want_col = TRUE;
  1210.     pos = searchc(c2, dir, (c1 == 't'), IDEF1PRENUM);
  1211.     break;
  1212.  
  1213.     case ',':
  1214.     case ';':
  1215.     /*
  1216.      * This should be FALSE for a backward motion.
  1217.      * How do we know it's a backward motion?
  1218.      *
  1219.      * Fix it later.
  1220.      */
  1221.     mtype = m_incl;
  1222.     curwin->w_set_want_col = TRUE;
  1223.     pos = crepsearch(curbuf, c1 == ',', IDEF1PRENUM);
  1224.     break;
  1225.     }
  1226.     if (pos == NULL) {
  1227.     retval = FALSE;
  1228.     } else {
  1229.     move_cursor(curwin, pos->p_line, pos->p_index);
  1230.     }
  1231.     return(retval);
  1232. }
  1233.  
  1234. /*
  1235.  * Handle adjust window command ('z').
  1236.  */
  1237. static bool_t
  1238. do_z(c1, c2)
  1239. int    c1, c2;
  1240. {
  1241.     Line    *lp;
  1242.     int    l;
  1243.     int    znum;
  1244.  
  1245.     switch (c2) {
  1246.     case '\n':                /* put cursor at top of screen */
  1247.     case '\r':
  1248.     znum = 1;
  1249.     break;
  1250.  
  1251.     case '.':                /* put cursor in middle of screen */
  1252.     znum = curwin->w_nrows / 2;
  1253.     break;
  1254.  
  1255.     case '-':                /* put cursor at bottom of screen */
  1256.     znum = curwin->w_nrows - 1;
  1257.     break;
  1258.  
  1259.     default:
  1260.     return(FALSE);
  1261.     }
  1262.  
  1263.     if (Prenum > 0) {
  1264.     do_goto(Prenum);
  1265.     }
  1266.     lp = curwin->w_cursor->p_line;
  1267.     for (l = 0; l < znum && lp != curbuf->b_line0; ) {
  1268.     l += plines(curwin, lp);
  1269.     curwin->w_topline = lp;
  1270.     lp = lp->l_prev;
  1271.     }
  1272.     cursupdate(curwin);
  1273.     update_window(curwin);
  1274.  
  1275.     return(TRUE);
  1276. }
  1277.  
  1278. /*
  1279.  * Handle character delete commands ('x' or 'X').
  1280.  */
  1281. static bool_t
  1282. do_x(c1, c2)
  1283. int    c1, c2;
  1284. {
  1285.     Posn    *curp;
  1286.     Posn    lastpos;
  1287.     int        nchars;
  1288.     int        i;
  1289.  
  1290.     nchars = IDEF1PRENUM;
  1291.     Redo.r_mode = r_normal;
  1292.     flexclear(&Redo.r_fb);
  1293.     (void) lformat(&Redo.r_fb, "%d%c", nchars, c1);
  1294.     curp = curwin->w_cursor;
  1295.  
  1296.     if (c1 == 'X') {
  1297.     for (i = 0; i < nchars && one_left(curwin, FALSE); i++)
  1298.         ;
  1299.     nchars = i;
  1300.     if (nchars == 0) {
  1301.         beep(curwin);
  1302.         return(TRUE);
  1303.     }
  1304.  
  1305.     } else /* c1 == 'x' */ {
  1306.     char    *line;
  1307.  
  1308.     /*
  1309.      * Ensure that nchars is not too big.
  1310.      */
  1311.     line = curp->p_line->l_text + curp->p_index;
  1312.     for (i = 0; i < nchars && line[i] != '\0'; i++)
  1313.         ;
  1314.     nchars = i;
  1315.  
  1316.     if (curp->p_line->l_text[0] == '\0') {
  1317.         /*
  1318.          * Can't do it on a blank line.
  1319.          */
  1320.         beep(curwin);
  1321.         return(TRUE);
  1322.     }
  1323.     }
  1324.  
  1325.     lastpos.p_line = curp->p_line;
  1326.     lastpos.p_index = curp->p_index + nchars - 1;
  1327.     yp_push_deleted();
  1328.     (void) do_yank(curbuf, curp, &lastpos, TRUE, cur_yp_name);
  1329.     replchars(curwin, curp->p_line, curp->p_index, nchars, "");
  1330.     if (curp->p_line->l_text[curp->p_index] == '\0') {
  1331.     (void) one_left(curwin, FALSE);
  1332.     }
  1333.     updateline(curwin);
  1334.     return(TRUE);
  1335. }
  1336.  
  1337. /*
  1338.  * Handle home ('H') end of page ('L') and middle line ('M') motion commands.
  1339.  */
  1340. static bool_t
  1341. do_HLM(c1, c2)
  1342. int    c1, c2;
  1343. {
  1344.     register bool_t    (*mvfunc) P((Xviwin *, long));
  1345.     register long    n;
  1346.  
  1347.     if (c1 == K_HOME) {
  1348.     c1 = 'H';
  1349.     }
  1350.  
  1351.     /*
  1352.      * Silly to specify a number before 'H' or 'L'
  1353.      * which would move us off the screen.
  1354.      */
  1355.     if (Prenum >= curwin->w_nrows) {
  1356.     return(FALSE);
  1357.     }
  1358.     
  1359.     move_cursor(curwin, (c1 == 'L') ? curwin->w_botline->l_prev :
  1360.                         curwin->w_topline, 0);
  1361.  
  1362.     switch (c1) {
  1363.     case 'H':
  1364.     mvfunc = onedown;
  1365.     n = Prenum - 1;
  1366.     break;
  1367.     case 'L':
  1368.     mvfunc = oneup;
  1369.     n = Prenum - 1;
  1370.     break;
  1371.     case 'M':
  1372.     mvfunc = onedown;
  1373.     n = (long) (curwin->w_nrows - 1) / 2;
  1374.     }
  1375.  
  1376.     (void) (*mvfunc)(curwin, n);
  1377.     begin_line(curwin, TRUE);
  1378.     return(TRUE);
  1379. }
  1380.  
  1381. /*
  1382.  * Handle '~' and CTRL('_') commands.
  1383.  */
  1384. static bool_t
  1385. do_rchar(c1, c2)
  1386. int    c1, c2;
  1387. {
  1388.     Posn    *cp;
  1389.     char    *tp;
  1390.     int        c;
  1391.     char    newc[2];
  1392.  
  1393.     Redo.r_mode = r_normal;
  1394.     flexclear(&Redo.r_fb);
  1395.     flexaddch(&Redo.r_fb, c1);
  1396.     cp = curwin->w_cursor;
  1397.     tp = cp->p_line->l_text;
  1398.     if (tp[0] == '\0') {
  1399.     /*
  1400.      * Can't do it on a blank line.
  1401.      */
  1402.     beep(curwin);
  1403.     return(FALSE);
  1404.     }
  1405.     c = tp[cp->p_index];
  1406.  
  1407.     switch (c1) {
  1408.     case '~':
  1409.     newc[0] = is_alpha(c) ?
  1410.             is_lower(c) ?
  1411.                 to_upper(c)
  1412.             : to_lower(c)
  1413.             : c;
  1414.     break;
  1415.     case CTRL('_'):            /* flip top bit */
  1416. #ifdef    TOP_BIT
  1417.     newc[0] = c ^ TOP_BIT;
  1418.     if (newc[0] == '\0') {
  1419.         newc[0] = c;
  1420.     }
  1421. #else    /* not TOP_BIT */
  1422.     beep(curwin);
  1423.     return(FALSE);
  1424. #endif    /* TOP_BIT */
  1425.     }
  1426.  
  1427.     newc[1] = '\0';
  1428.     replchars(curwin, cp->p_line, cp->p_index, 1, newc);
  1429.     updateline(curwin);
  1430.     (void) one_right(curwin, FALSE);
  1431.     return(FALSE);
  1432. }
  1433.  
  1434. /*
  1435.  * Handle commands which just go into insert mode
  1436.  * ('i', 'a', 'I', 'A', 'o', 'O').
  1437.  */
  1438. static bool_t
  1439. do_ins(c1, c2)
  1440. int    c1, c2;
  1441. {
  1442.     bool_t    startpos = TRUE;    /* FALSE means start position moved */
  1443.  
  1444.     if (!start_command(curwin)) {
  1445.     return(FALSE);
  1446.     }
  1447.  
  1448.     Redo.r_mode = r_insert;
  1449.     flexclear(&Redo.r_fb);
  1450.     flexaddch(&Redo.r_fb, c1);
  1451.  
  1452.     switch (c1) {
  1453.     case 'o':
  1454.     case 'O':
  1455.     if (((c1 == 'o') ? openfwd(FALSE) : openbwd()) == FALSE) {
  1456.         beep(curwin);
  1457.         end_command(curwin);
  1458.         return(FALSE);
  1459.     }
  1460.     break;
  1461.  
  1462.     case 'I':
  1463.     begin_line(curwin, TRUE);
  1464.     startpos = FALSE;
  1465.     break;
  1466.  
  1467.     case 'A':
  1468.     while (one_right(curwin, TRUE))
  1469.         ;
  1470.     startpos = FALSE;
  1471.     break;
  1472.  
  1473.     case 'a':
  1474.     /*
  1475.      * 'a' works just like an 'i' on the next character.
  1476.      */
  1477.     (void) one_right(curwin, TRUE);
  1478.     startpos = FALSE;
  1479.     }
  1480.  
  1481.     startinsert(startpos);
  1482.     return(FALSE);
  1483. }
  1484.  
  1485. /*
  1486.  * Handle a shift operation. The prenum and operator/operands are
  1487.  * passed, along with the first and last positions to be shifted.
  1488.  */
  1489. static void
  1490. op_shift(op, c1, c2, num, top, bottom)
  1491. int    op;
  1492. int    c1, c2;
  1493. long    num;
  1494. Posn    *top;
  1495. Posn    *bottom;
  1496. {
  1497.     /*
  1498.      * Do the shift.
  1499.      */
  1500.     tabinout(op, top->p_line, bottom->p_line);
  1501.  
  1502.     /*
  1503.      * Put cursor on first non-white of line; this is good if the
  1504.      * cursor is in the range of lines specified, which is normally
  1505.      * will be.
  1506.      */
  1507.     begin_line(curwin, TRUE);
  1508.     update_buffer(curbuf);
  1509.  
  1510.     /*
  1511.      * Construct redo buffer.
  1512.      */
  1513.     Redo.r_mode = r_normal;
  1514.     flexclear(&Redo.r_fb);
  1515.     if (num != 0) {
  1516.     (void) lformat(&Redo.r_fb, "%c%ld%c%c", op, num, c1, c2);
  1517.     } else {
  1518.     (void) lformat(&Redo.r_fb, "%c%c%c", op, c1, c2);
  1519.     }
  1520. }
  1521.  
  1522. /*
  1523.  * op_delete - handle a delete operation
  1524.  * The characters "c1" and "c2" are the target character and
  1525.  * its argument (if it takes one) respectively. E.g. 'f', '/'.
  1526.  * The "num" argument is the numeric prefix.
  1527.  */
  1528. static void
  1529. op_delete(c1, c2, num, top, bottom)
  1530. int    c1, c2;
  1531. long    num;
  1532. Posn    *top;
  1533. Posn    *bottom;
  1534. {
  1535.     long    nlines;
  1536.     int        n;
  1537.  
  1538.     /*
  1539.      * If the target is non-inclusive, move back a character.
  1540.      * We assume it is okay to do this, and it seems to work,
  1541.      * so no checking is performed at the moment.
  1542.      */
  1543.     if (mtype == m_nonincl) {
  1544.     (void) dec(bottom);
  1545.     }
  1546.  
  1547.     nlines = cntllines(top->p_line, bottom->p_line);
  1548.  
  1549.     /*
  1550.      * Do a yank of whatever we're about to delete. If there's too much
  1551.      * stuff to fit in the yank buffer, disallow the delete, since we
  1552.      * probably wouldn't have enough memory to do it anyway.
  1553.      */
  1554.     yp_push_deleted();
  1555.     if (!do_yank(curbuf, top, bottom, (mtype != m_line), cur_yp_name)) {
  1556.     show_error(curwin, "Not enough memory to perform delete");
  1557.     return;
  1558.     }
  1559.  
  1560.     if (mtype == m_line) {
  1561.     /*
  1562.      * Put the cursor at the start of the section to be deleted
  1563.      * so that repllines will correctly update it and the screen
  1564.      * pointer, and update the screen.
  1565.      */
  1566.     move_cursor(curwin, top->p_line, 0);
  1567.     repllines(curwin, top->p_line, nlines, (Line *) NULL);
  1568.     begin_line(curwin, TRUE);
  1569.     } else {
  1570.     /*
  1571.      * After a char-based delete, the cursor should always be
  1572.      * on the character following the last character of the
  1573.      * section being deleted. The easiest way to achieve this
  1574.      * is to put it on the character before the section to be
  1575.      * deleted (which will not be affected), and then move one
  1576.      * place right afterwards.
  1577.      */
  1578.     move_cursor(curwin, top->p_line,
  1579.                 top->p_index - ((top->p_index > 0) ? 1 : 0));
  1580.  
  1581.     if (top->p_line == bottom->p_line) {
  1582.         /*
  1583.          * Delete characters within line.
  1584.          */
  1585.         n = (bottom->p_index - top->p_index) + 1;
  1586.         replchars(curwin, top->p_line, top->p_index, n, "");
  1587.     } else {
  1588.         /*
  1589.          * Character-based delete between lines.
  1590.          * So we actually have to do three deletes;
  1591.          * one to delete to the end of the top line,
  1592.          * one to delete the intervening lines, and
  1593.          * one to delete up to the target position.
  1594.          */
  1595.         if (!start_command(curwin)) {
  1596.         return;
  1597.         }
  1598.  
  1599.         /*
  1600.          * First delete part of the last line.
  1601.          */
  1602.         replchars(curwin, bottom->p_line, 0, bottom->p_index + 1, "");
  1603.  
  1604.         /*
  1605.          * Now replace the rest of the top line with the
  1606.          * remainder of the bottom line.
  1607.          */
  1608.         replchars(curwin, top->p_line, top->p_index, INT_MAX,
  1609.                         bottom->p_line->l_text);
  1610.  
  1611.         /*
  1612.          * Finally, delete all lines from (top + 1) to bot,
  1613.          * inclusive.
  1614.          */
  1615.         repllines(curwin, top->p_line->l_next,
  1616.                 cntllines(top->p_line, bottom->p_line) - 1,
  1617.                 (Line *) NULL);
  1618.  
  1619.         end_command(curwin);
  1620.     }
  1621.  
  1622.     if (top->p_index > 0) {
  1623.         (void) one_right(curwin, FALSE);
  1624.     }
  1625.     }
  1626.  
  1627.     /*
  1628.      * Construct redo buffer.
  1629.      */
  1630.     Redo.r_mode = r_normal;
  1631.     flexclear(&Redo.r_fb);
  1632.     if (num != 0) {
  1633.     (void) lformat(&Redo.r_fb, "d%ld%c%c", num, c1, c2);
  1634.     } else {
  1635.     (void) lformat(&Redo.r_fb, "d%c%c", c1, c2);
  1636.     }
  1637.  
  1638.     if (mtype != m_line && nlines == 1) {
  1639.     updateline(curwin);
  1640.     } else {
  1641.     update_buffer(curbuf);
  1642.     }
  1643. }
  1644.  
  1645. /*
  1646.  * op_change - handle a change operation
  1647.  */
  1648. static void
  1649. op_change(c1, c2, num, top, bottom)
  1650. int    c1, c2;
  1651. long    num;
  1652. Posn    *top;
  1653. Posn    *bottom;
  1654. {
  1655.     bool_t    doappend;    /* true if we should do append, not insert */
  1656.  
  1657.     /*
  1658.      * Start the command here so the initial delete gets
  1659.      * included in the meta-command and hence undo will
  1660.      * work properly.
  1661.      */
  1662.     if (!start_command(curwin)) {
  1663.     return;
  1664.     }
  1665.  
  1666.     if (mtype == m_line) {
  1667.     long    nlines;
  1668.     Line    *lp;
  1669.  
  1670.     /*
  1671.      * This is a bit awkward ... for a line-based change, we don't
  1672.      * actually delete the whole range of lines, but instead leave
  1673.      * the first line in place and delete its text after the cursor
  1674.      * position. However, yanking the whole thing is probably okay.
  1675.      */
  1676.     yp_push_deleted();
  1677.     if (!do_yank(curbuf, top, bottom, FALSE, cur_yp_name)) {
  1678.         show_error(curwin, "Not enough memory to perform change");
  1679.         return;
  1680.     }
  1681.  
  1682.     lp = top->p_line;
  1683.  
  1684.     nlines = cntllines(lp, bottom->p_line);
  1685.     if (nlines > 1) {
  1686.         repllines(curwin, lp->l_next, nlines - 1, (Line *) NULL);
  1687.     }
  1688.  
  1689.     move_cursor(curwin, lp, 0);
  1690.  
  1691.     /*
  1692.      * This is not right; it won't do the right thing when
  1693.      * the cursor is in the whitespace of an indented line.
  1694.      * However, it will do for the moment.
  1695.      */
  1696.     begin_line(curwin, TRUE);
  1697.  
  1698.     replchars(curwin, lp, curwin->w_cursor->p_index,
  1699.                         strlen(lp->l_text), "");
  1700.     update_buffer(curbuf);
  1701.     } else {
  1702.     /*
  1703.      * A character-based change really is just a delete and an insert.
  1704.      * So use the deletion code to make things easier.
  1705.      */
  1706.     doappend = (mtype == m_incl) && endofline(bottom);
  1707.  
  1708.     op_delete(c1, c2, num, top, bottom);
  1709.  
  1710.     if (doappend) {
  1711.         (void) one_right(curwin, TRUE);
  1712.     }
  1713.     }
  1714.  
  1715.     Redo.r_mode = r_insert;
  1716.     flexclear(&Redo.r_fb);
  1717.     if (num != 0) {
  1718.     (void) lformat(&Redo.r_fb, "c%ld%c%c", num, c1, c2);
  1719.     } else {
  1720.     (void) lformat(&Redo.r_fb, "c%c%c", c1, c2);
  1721.     }
  1722.  
  1723.     startinsert(FALSE);
  1724. }
  1725.  
  1726. static void
  1727. op_yank(top, bottom)
  1728. Posn    *top;
  1729. Posn    *bottom;
  1730. {
  1731.     long    nlines;
  1732.  
  1733.     /*
  1734.      * Report on the number of lines yanked.
  1735.      */
  1736.     nlines = cntllines(top->p_line, bottom->p_line);
  1737.  
  1738.     if (nlines > Pn(P_report)) {
  1739.     show_message(curwin, "%ld lines yanked", nlines);
  1740.     }
  1741.  
  1742.     /*
  1743.      * If the target is non-inclusive and character-based,
  1744.      * reduce the final position by one.
  1745.      */
  1746.     if (mtype == m_nonincl) {
  1747.     (void) dec(bottom);
  1748.     }
  1749.  
  1750.     (void) do_yank(curbuf, top, bottom, (mtype != m_line), cur_yp_name);
  1751. }
  1752.  
  1753. static bool_t
  1754. dojoin()
  1755. {
  1756.     register Posn    *curr_pos;    /* cursor position (abbreviation) */
  1757.     register Line    *curr_line;    /* line we started the join on */
  1758.     char        *nextline;    /* text of subsequent line */
  1759.     int            join_index;    /* index of start of new text section */
  1760.     int            size1;        /* size of the first line */
  1761.     int            size2;        /* size of the second line */
  1762.  
  1763.     curr_pos = curwin->w_cursor;
  1764.     curr_line = curr_pos->p_line;
  1765.  
  1766.     /*
  1767.      * If we are on the last line, we can't join.
  1768.      */
  1769.     if (curr_line->l_next == curbuf->b_lastline)
  1770.     return(FALSE);
  1771.  
  1772.     if (!start_command(curwin)) {
  1773.     return(FALSE);
  1774.     }
  1775.  
  1776.     /*
  1777.      * Move cursor to end of line, and find out
  1778.      * exactly where we will place the new text.
  1779.      */
  1780.     while (one_right(curwin, FALSE))
  1781.     ;
  1782.     join_index = curr_pos->p_index;
  1783.     if (curr_line->l_text[join_index] != '\0')
  1784.     join_index += 1;
  1785.  
  1786.     /*
  1787.      * Copy the text of the next line after the end of the
  1788.      * current line, but don't copy any initial whitespace.
  1789.      * Then delete the following line.
  1790.      */
  1791.     nextline = curr_line->l_next->l_text;
  1792.     while (*nextline == ' ' || *nextline == '\t')
  1793.     nextline++;
  1794.     size1 = strlen(curr_line->l_text);
  1795.     size2 = strlen(nextline);
  1796.  
  1797.     replchars(curwin, curr_line, join_index, 0, nextline);
  1798.     repllines(curwin, curr_line->l_next, (long) 1, (Line *) NULL);
  1799.  
  1800.     if (size1 != 0 && size2 != 0) {
  1801.     /*
  1802.      * If there is no whitespace on this line,
  1803.      * insert a single space.
  1804.      */
  1805.     if (gchar(curr_pos) != ' ' && gchar(curr_pos) != '\t') {
  1806.         replchars(curwin, curr_line, curr_pos->p_index + 1, 0, " ");
  1807.     }
  1808.  
  1809.     /*
  1810.      * Make sure the cursor sits on the right character.
  1811.      */
  1812.     (void) one_right(curwin, FALSE);
  1813.     }
  1814.  
  1815.     end_command(curwin);
  1816.  
  1817.     update_buffer(curbuf);
  1818.  
  1819.     return(TRUE);
  1820. }
  1821.